// Copyright (C) 2010 Razvan Constantin <razvan.constantin@rconst.net>
//
// This file is part of FileBow.
//
// FileBow is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// FileBow is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FileBow.  If not, see <http://www.gnu.org/licenses/>.

#include "utils/npath.h"

#include <errno.h>
#include <limits.h>
#include <stack>
#include <string>
#include <unistd.h>  // TODO: ifdef POSIX

#include <Poco/File.h>
#include <Poco/Path.h>

using namespace std;
using Poco::File;
using Poco::Path;

NPath::NPath(const char* path) {
  Init(Path(path));
}

NPath::NPath(const string& path) {
  Init(Path(path));
}

NPath::NPath(const Path& path) {
  Init(path);
}

void NPath::Init(const Path& path) {
  mPath = path;
  if (!mPath.isFile()) {
    mPath.makeFile();
  }
  File(mPath).swap(mFile);
}

NPath::NPath(const NPath& npath) {
  Init(npath.mPath);
}

NPath& NPath::operator=(const NPath& npath) {
  Init(npath.mPath);
  return *this;
}

void NPath::Up() {
  mPath.makeParent();
  mPath.makeFile();
  File(mPath).swap(mFile);
}

void NPath::Down(const string& node) {
  mPath.makeDirectory();
  mPath.setFileName(node);
  File(mPath).swap(mFile);
}

bool ReadLinkStr(const string& link, string* ref) {
  char buf[PATH_MAX];
  int ref_size = readlink(link.c_str(), buf, sizeof(buf));
  if (ref_size < 0) {
    LOG(WARN) << "Cannot readlink " << link << ": " << errno;
    return false;
  }
  ref->assign(buf, ref_size);
  return true;
}

void NPath::ReadLink() {
  if (!valid() || !handle().exists()) {
    return;
  }
  stack<string> nodes;
  for (; valid() && !handle().isLink(); Up()) {
    nodes.push(node());
  }
  if (valid() && handle().isLink()) {
    string ref;
    if (ReadLinkStr(ToString(), &ref)) {
      Init(Path(ref));
    }
  }
  for (; !nodes.empty(); nodes.pop()) {
    Down(nodes.top());
  }
}
